enum STATUS {
    pending = 'pending',
    fulfilled = 'fulfilled',
    rejected = 'rejected'
}

// 统一处理x的值
function resolvePromise(promise2, x) {
    // 如果promise2和x的值相等（在onFulfilled/onRejected返回了自己的promise），则直接报错
    if (promise2 == x) {
        return promise2.reject(new TypeError('不能返回自己的Promise'))
    }
    // 如果x是引用类型
    if ((x !== null && typeof x === 'object') || typeof x === 'function') {
        // 这里是一个标识，如果调用了resolve或reject则不能再次调用(有可能是别人的Promise)
        let called = false
        try {
            // 尝试取x的then方法，但可能会报错
            const then = x.then
            // 如果then是一个函数, 则执行这个函数，并让它的this指向x
            if (typeof then === 'function') {
                then.call(x, y => {
                    // 如果调用过了，则不能再次调用
                    if (called) return
                    called = true
                    // y有可能也是一个promise，所以递归解析y 直到他是一个普通值为止
                    resolvePromise(promise2, y)
                }, r => {
                    if (called) return
                    called = true
                    promise2.reject(r)
                })
            }
            // 如果then不是一个函数,说明是一个普通对象，则直接resolve
            else {
                promise2.resolve(x)
            }
        } catch (e) {
            // 如果调用过了reject，则不能再次调用
            if (called) return
            called = true
            // 如果报错，则直接reject
            promise2.reject(e)
        }
    }
    // 如果x是一个普通值，则直接resolve
    else {
        promise2.resolve(x)
    }
}

class Promise {
    private status: STATUS = STATUS.pending;
    private value: unknown;
    private reason: unknown;
    private fulfilledCallbacks: Function[] = []
    private rejectedCallbacks: Function[] = []
    private resolve: Function;
    private reject: Function;
    static deferred: () => void;

    constructor(executor: Function) {
        if (typeof executor !== 'function') {
            throw TypeError('executor must an function')
        }
        const resolve = (value: unknown) => {
            if (this.status === STATUS.pending) {
                this.status = STATUS.fulfilled
                this.value = value
                this.fulfilledCallbacks.forEach(f => f())
            }
        }

        const reject = (error: unknown) => {
            if (this.status === STATUS.pending) {
                this.status = STATUS.rejected
                this.reason = error;
                this.rejectedCallbacks.forEach(f => f())
            }
        }

        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }
    }

    then(onFulfilled?: Function, onRejected?: Function) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
        onRejected = typeof onRejected === 'function' ? onRejected : r => {
            throw r
        };
        const promise2 = new Promise((resolve, reject) => {
            if (this.status === STATUS.fulfilled) {
                // 如果使用同步，promise2是拿不到的
                setTimeout(() => {
                    try {
                        const x = onFulfilled(this.value)
                        // x 可能不是一个基本类型的值
                        promise2.resolve = resolve
                        promise2.reject = reject
                        resolvePromise(promise2, x)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === STATUS.rejected) {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.reason)
                        promise2.resolve = resolve
                        promise2.reject = reject
                        resolvePromise(promise2, x)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            if (this.status === STATUS.pending) {
                this.fulfilledCallbacks.push(() => {
                    // 虽然这里已经是异步的了，但为了和原生Promise表现一致，也使用异步包起来
                    setTimeout(() => {
                        try {
                            const x = onFulfilled(this.value)
                            promise2.resolve = resolve
                            promise2.reject = reject
                            resolvePromise(promise2, x)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
                this.rejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            const x = onRejected(this.reason)
                            promise2.resolve = resolve
                            promise2.reject = reject
                            resolvePromise(promise2, x)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            }
        })
        return promise2
    }

    catch(onRejected: Function) {
        return this.then(null, onRejected)
    }
}

interface dfdType {
    promise: Promise,
    resolve: Function,
    reject: Function
}

Promise.deferred = function () {
    const dfd = {} as dfdType
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}
export default Promise